/**
 * @class 小行星实体
 * @author DuskyHuang 圣子
*/

import { _decorator, Camera, Node, Vec3, MeshRenderer, Mesh } from 'cc';
import { DEG_PERIGON } from 'const';
import { PlanetaryProxy, timer, UIProxy } from 'proxies';
import { ICelestial } from '../celestial';
import { CustomizedLOD } from '../entities/customized-lod';
const { ccclass, property } = _decorator;

@ccclass('cocosmos.Asteroid')
export class Asteroid extends CustomizedLOD implements ICelestial {

    @property([Mesh]) public meshTypeA: Mesh[] = [];
    @property([Mesh]) public meshTypeB: Mesh[] = [];
    @property(MeshRenderer) public mesh0: MeshRenderer;
    @property(MeshRenderer) public mesh1: MeshRenderer;

    public cat = 'asteroid' as any;
    protected get LODValves(): num.pct[] { return ASTEROID_LOD_VALVES };
    public activating: boolean = true;

    public set rectangular(arg: boolean) {
        if (this._rectangular === arg) return;
        this._rectangular = arg;
        if (arg) {
            this.node.setScale(this._rectParams.size);
            this.node.setPosition(this._rectParams.pos);
        } else {
            this.node.setScale(this._real);
        }
    }

    public loadConfig(config: beltConfig, viewer: Camera, base: null, sun: ICelestial): this {
        this.node.name = 'Asteroid';
        this._config = config;
        this._center = sun.node;
        this._viewer = viewer;
        this.initPhysicsParams();
        return this;
    }

    protected init(): void {
        this._rotator = this.node.getChildByName('Rotator')!;
        this._randomMesh();
    }

    private _randomMesh(): void {
        const type = Math.randomBool() ? this.meshTypeA : this.meshTypeB;
        this.mesh0.mesh = type[0];
        this.mesh1.mesh = type[1];
    }

    protected onTick(dt?: time.sec, tick?: num.int): void {
        const span = dt * timer.timescale;
        this.step(span);
        this.rotate(span);
        this.revolve(span);
        this.syncActivating();
    }

    protected step(span: time.sec): void {
        this._orbitalAngle += this._orbitalVelocity * span;
        this._rotateAngle += this._rotateVelocity * span;
        if (this._orbitalAngle > DEG_PERIGON) this._orbitalAngle -= DEG_PERIGON;
        if (this._rotateAngle > DEG_PERIGON) this._rotateAngle -= DEG_PERIGON;
    }

    protected rotate(span?: time.sec): void {
        this._rotator.activeInHierarchy && this._rotator.children.forEach(child => {
            child.active && child.setRotationFromEuler(0, this._rotateAngle, 0);
        });
        this._lod2.forward = Vec3.subtract(v3a, this.node.worldPosition, this._center.worldPosition)
    }

    protected revolve(span?: time.sec): void {
        if (!this._rectangular) {
            const { _dist } = this, rad = Math.parseRadian(this._orbitalAngle);
            const [cosA, sinA] = [Math.cos(rad), Math.sin(rad)];
            this.node.setPosition(_dist * sinA, 0, _dist * cosA);
        }
    }

    protected syncActivating(): void {
        if (!this.node.parent || !this.node.parent.activeInHierarchy) return;
        if (!this.activating) { this.node.active = false; return; }
        this.node.active = this.activating && Vec3.squaredDistance(this.node.worldPosition, this._center.worldPosition) > NOT_IMP_CUL_VAV_SQR;
    }

    private initPhysicsParams(): void {
        const { ranges: { diam, dist, angular, scale } } = this._config;
        const [min, max] = diam;
        const [from, to] = dist;
        const mid = Math.avg(from, to), w = mid / 8;
        const [dec, acc] = scale;
        const [ant, clk] = angular
        this._dist = mid + Math.randomRange(-w, w);
        this._orbitalAngle = Math.randomRange(0, 360);
        this._orbitalVelocity = VelFactor / Math.sqrt(this._dist ** 3);
        // 随机三维大小
        const s = Math.randomRange(min, max);
        this._real.set(Math.randomRange(dec, acc) * s, Math.randomRange(dec, acc) * s, Math.randomRange(dec, acc) * s)
        this.updateScale();
        // 自转信息
        this._rotateVelocity = Math.randomRange(ant, clk);
        this._rotateAngle = Math.randomRange(0, 360);
        this._rotator.setRotationFromEuler(Math.randomRange(-90, 90), 0, Math.randomRange(-90, 90));
        // 局部数据
        this._initRectangularParams();
    }

    private _initRectangularParams(): void {
        const { BASE_SIZE, SIZE_RANGE: [from, to], SCALE_RANGE: [min, max], POS_RANGE: [X, Y, Z] } = AsteroidRectangular;
        const avg = BASE_SIZE * Math.randomRange(from, to);
        const [x, y, z] = [Math.randomRange(min, max), Math.randomRange(min, max), Math.randomRange(min, max)];
        this._rectParams.size.set(avg * x, avg * y, avg * z);
        this._rectParams.pos.set(Math.randomRange(-X, X), Math.randomRange(-Y, Y), Math.randomRange(-Z, Z));
    }

    protected updateScale(magnified?: num.pct): void {
        if (!this._rectangular) super.updateScale(magnified);
    }

    private _dist: ykm = 1;
    private _orbitalVelocity: num.deg = 0;
    private _rotateVelocity: num.deg = 0;
    private _orbitalAngle: num.deg = 0;
    private _rotateAngle: num.deg = 0;
    private _config: beltConfig;
    private _rotator!: Node;
    private _center: Node;
    private _rectangular: boolean = false;
    private _rectParams: RectangularParams = { size: new Vec3, pos: new Vec3 };
    protected onLod0Initiated(): void { }
}

const ASTEROID_LOD_VALVES = [3, 1, 0.3];
const { WPS_CULLING_VALVE } = UIProxy;
const { AsteroidRectangular } = PlanetaryProxy;
const CUL_VAV_SQR = WPS_CULLING_VALVE * WPS_CULLING_VALVE;
const NOT_IMP_CUL_VAV_SQR = CUL_VAV_SQR * 10;
const v3a = new Vec3;

const VelFactor = Math.sqrt(1.4959 ** 3) / 365.25; // ω = sqrt(R^3/r^3)/P
declare type RectangularParams = { size: Vec3, pos: Vec3 };